// Package config provides configuration management for the libvirt exporter
package config

import (
	"fmt"

	"gitee.com/openeuler/uos-libvirtd-exporter/slogx"
	"github.com/spf13/viper"
)

// Config represents the complete configuration for the exporter
type Config struct {
	Libvirt LibvirtConfig `mapstructure:"libvirt"` // libvirt connection configuration
	Web     WebConfig     `mapstructure:"web"`     // web server configuration
	Log     LogConfig     `mapstructure:"log"`     // logging configuration
}

// LibvirtConfig represents the libvirt connection configuration
type LibvirtConfig struct {
	URI string `mapstructure:"uri"` // libvirt connection URI
}

// WebConfig represents the web server configuration
type WebConfig struct {
	ListenAddress        string `mapstructure:"listen_address"`         // address to listen on
	TelemetryPath        string `mapstructure:"telemetry_path"`         // path to expose metrics
	ExposeRuntimeMetrics bool   `mapstructure:"expose_runtime_metrics"` // whether to expose runtime metrics
}

// LogConfig represents the logging configuration
type LogConfig struct {
	FilePath  string `mapstructure:"file_path"`     // path to log file (empty means log to stdout/stderr only)
	Level     string `mapstructure:"level"`         // log level (debug, info, warn, error)
	Format    string `mapstructure:"format"`        // log format (text, json)
	EnableStd bool   `mapstructure:"enable_stdout"` // whether to also log to stdout/stderr
}

// Load loads configuration from file or uses defaults
// configFile: If non-empty, specifies the config file to use; otherwise auto-search
func Load(configFile string) (*Config, string, error) {
	return LoadWithViper(viper.GetViper(), configFile)
}

// LoadWithViper loads configuration using a specific viper instance
func LoadWithViper(v *viper.Viper, configFile string) (*Config, string, error) {
	slogx.Debug("Loading configuration with viper")

	// Set default configuration values
	v.SetDefault("libvirt.uri", "qemu:///system")
	v.SetDefault("web.listen_address", ":9177")
	v.SetDefault("web.telemetry_path", "/metrics")
	v.SetDefault("web.expose_runtime_metrics", false)
	v.SetDefault("log.file_path", "")  // empty means log to stdout/stderr only
	v.SetDefault("log.level", "info")  // default log level
	v.SetDefault("log.format", "text") // default log format

	// Load configuration from file if specified
	if configFile != "" {
		// User explicitly specified config file
		slogx.Debug("Loading configuration from specified file", "file", configFile)
		v.SetConfigFile(configFile)
		if err := v.ReadInConfig(); err != nil {
			return nil, "", fmt.Errorf("failed to read config file %q: %w", configFile, err)
		}
	} else {
		// Auto-search for config file in current directory and system directory
		slogx.Debug("Searching for configuration file in default locations")
		v.SetConfigName("config")
		v.SetConfigType("yaml")
		v.AddConfigPath(".")                          // current directory
		v.AddConfigPath("/etc/uos-libvirtd-exporter") // system directory

		if err := v.ReadInConfig(); err != nil {
			if _, ok := err.(viper.ConfigFileNotFoundError); ok {
				// Config file not found, use defaults
				slogx.Info("No config file found, using defaults")
			} else {
				return nil, "", fmt.Errorf("error reading config: %w", err)
			}
		}
	}

	// Unmarshal configuration into struct
	var cfg Config
	if err := v.Unmarshal(&cfg); err != nil {
		return nil, "", fmt.Errorf("failed to unmarshal config: %w", err)
	}

	// Get the path of the config file that was actually used
	usedFile := ""
	if v.ConfigFileUsed() != "" {
		usedFile = v.ConfigFileUsed()
		slogx.Debug("Configuration file loaded", "file", usedFile)
	}

	return &cfg, usedFile, nil
}

// GetListenAddr returns the listen address for the web server
func (c *Config) GetListenAddr() string {
	return c.Web.ListenAddress
}

// GetMetricsPath returns the metrics path for the web server
func (c *Config) GetMetricsPath() string {
	return c.Web.TelemetryPath
}

// ExposeRuntimeMetrics returns whether to expose runtime metrics
func (c *Config) ExposeRuntimeMetrics() bool {
	return c.Web.ExposeRuntimeMetrics
}

// GetLogFilePath returns the log file path
func (c *Config) GetLogFilePath() string {
	return c.Log.FilePath
}
